#include <ime.h>
#include <string.h>
#define MAXWORDS 0x20000
//Gloabal Data
char* words[MAXWORDS];
int   frequency[MAXWORDS];
int   tree   [MAXWORDS*2-1][10];
int nwords;
//Context Private Data
typedef struct {
	int cand[10];
	char candbuf[10][50];
	int ncand;
	int page;
	const char* preedit;
} IC;
int max(int x,int y){return x>y?x:y;}
int min(int x,int y){return x<y?x:y;}
void increase_frequency(int id, int T, int L, int R)
{
	if(R - L == 1)
	{
		tree[T][0] = id;
		frequency[id] ++;
		return;
	}
	int M = (L + R)/2;
	if(id < M) increase_frequency(id, T*2+1, L, M);
	else	   increase_frequency(id, T*2+2, M, R);
	int i , j;
	for(i = 0; i < 10 && tree[T][i] != -1; i ++)
	{
		if(tree[T][i] == id) return;
		if(frequency[tree[T][i]] > frequency[id] ) continue;
		if(frequency[tree[T][i]] == frequency[id] && tree[T][i] < id) continue;
		break;
	}
	for(j = 9; j > i; j --) tree[T][j] = tree[T][j-1];
	tree[T][i] = id;	
}
int query(int l, int r, int T,int L, int R, int* res)
{
	int i;
	if(l == L && r == R)
	{
		for(i = 0 ; i < 10; i ++) res[i] = tree[T][i];
		return 1;
	}
	int ret[2][10] = {{-1},{-1}};
	int M = (L + R)/2;
	if(l < min(r,M)) query(l,min(r,M),T*2+1,L,M,ret[0]);
	if(max(l,M) < r) query(max(l,M),r,T*2+2,M,R,ret[1]);
	int j[2],k;
	for(j[0] = j[1] = k = 0; k < 10; k ++)
	{
		int u , maxu, maxid = -1;
		for(u = 0; u < 2; u ++)
			if(ret[u][j[u]] != -1)
			{
				if(maxid != -1 && frequency[maxid] > frequency[ret[u][j[u]]]) continue;
				if(maxid != -1 && frequency[maxid] == frequency[ret[u][j[u]]] &&
				   maxid < ret[u][j[u]]) continue;
				maxid = ret[u][j[u]];
				maxu  = u;
			}
		if(maxid == -1) break;
		res[k] = maxid;
		j[maxu] ++;
	}
	if(k <  10) res[k] = -1;
	return k;
}
int search(int* L, int* R, char* str)
{
	int l = 0 , r = nwords;
	int i;
	for(i = 0; str[i]; i ++)
	{
		int tl = l, tr = r;
		if(words[l][i] > str[i]) return -1;
		if(words[l][i] != str[i])
		{
			while(tr-tl > 1)
			{
				int m = (tl+tr)/2;
				if(str[i] <= words[m][i]) tr = m;
				else tl = m;
			}
			if(tr - tl != 1) return -1;
			l = tr;
		}
		if(words[r-1][i] < str[i]) return -1;
		if(words[r-1][i] != str[i])
		{
			tr = r;
			while(tr-tl>1)
			{
				int m = (tl+tr)/2;
				if(str[i] < words[m][i])  tr = m;
				else tl = m;
			}
			if(tr - tl != 1) return -1;
			r = tr;
		}
	}
	*L = l;
	*R = r;
	return 0;
}
int Init(char* param,void* ic)
{
	nwords = 0;
	FILE* fp = fopen(param,"r");
	if(NULL == fp) return -1;
	char buffer[100];
	memset(frequency, 0, sizeof(frequency));
	memset(tree, -1, sizeof(tree));
	while(fgets(buffer,100,fp))
	{
		int len = strlen(buffer);
		while(len && (buffer[len-1] == '\n' || buffer[len-1] == '\r'))
			buffer[--len] = 0;
		if(len <= 0) continue;
		words[nwords] = (char*)malloc(len);
		strcpy(words[nwords],buffer);
		increase_frequency(nwords++, 0, 0, MAXWORDS);
	}
	return 0;
}
void commit(uint32_t id,void* ic)
{
	IC* imic = (IC*)ic;
	IMCommit(words[imic->cand[id]],ic);
	increase_frequency(imic->cand[id],0,0,MAXWORDS);
	imic->ncand = 0;
}
int NumSelect(uint32_t sym, uint32_t state, uint32_t id, void* data)
{
	IC* ic = (IC*)data;
	if(ic->ncand == 0) return FE_CLIENT;
	int num = sym - '0';
	if(num == 0) num = 10;
	int cid = num + ic->page * 5;
	if( cid > ic->ncand) return FE_SUCCEED;
	//IMCommit(words[ic->cand[cid-1]],data);
	commit(cid-1,data);
	return FE_SUCCEED;
}
int EntSelect(uint32_t sym, uint32_t state, uint32_t id, void* data);
int SpcSelect(uint32_t sym, uint32_t state, uint32_t id, void* data)
{
	IC* ic = (IC*)data;
	if(ic->ncand == 0) 
	{
		IMCommit((char*)ic->preedit,data);
		return FE_CLIENT;
	}
	int cid = ic->page * 5;
	if(cid >= ic->ncand) return FE_SUCCEED;
	//IMCommit(words[ic->cand[cid]],data);
	commit(cid,data);
	return FE_CLIENT;
}
int EntSelect(uint32_t sym, uint32_t state, uint32_t id,void* data)
{
	IC* ic = (IC*)data;
	if(ic->preedit == NULL || ic->preedit[0] == 0) return FE_CLIENT;
	IMCommit((char*)ic->preedit,data);
	ic->ncand = 0;
	return FE_SUCCEED;
}
void update_candidate(IC* ic)
{
	int i;
	char* tmp[5];
	static const char* fmt[] = {"<span color=\"red\">%d.%s</span>","%d.%s"};
	for(i = 0; i < 5 && ic->page*5 + i < ic->ncand; i ++)
	{
		snprintf(ic->candbuf[i],50,fmt[i!=0],i+1,words[ic->cand[ic->page*5+i]]);
		tmp[i] = ic->candbuf[i];
	}
	IMUpdateCandidates(tmp,i,(void*)ic);
}
int ChnPage(uint32_t sym, uint32_t state, uint32_t id,void* data)
{
	IC* ic = (IC*)data;
	if(ic->ncand == 0) return FE_CLIENT;
	if(sym == '-' && ic->page == 1) ic->page = 0;
	if(sym == '=' && ic->page == 0 && ic->ncand > 5) ic->page = 1;
	update_candidate(ic);
	return 0;
}
int InputChanged(char* text, uint32_t len ,void* data)
{
	IC* ic = (IC*)data;
	ic->preedit = text;
	if(len == 0) 
	{
		ic->ncand = 0;
		IMCommit("",data);
		return 0;
	}
	int l,r;
	if(search(&l,&r,text) != -1)
		ic->ncand = query(l,r,0,0,MAXWORDS,ic->cand);
	else ic->ncand = 0;
	ic->page = 0;
	update_candidate(ic);
	IMUpdatePrecommitText(text,data);
	return 0;
}
int Enabled(void* data)
{
	char* buf;
	IC* ic = (IC*)data;
	ic->ncand = 0;
	IMRegisterForwardFilter('0','9'+1,IMStateAll,IMStateNone,IMKeyActionPress, NumSelect, data);
	IMRegisterForwardFilter(' ',' '+1,IMStateAll,IMStateNone,IMKeyActionPress, SpcSelect,data);
	IMRegisterForwardFilter(0xff0d,0xff0e,IMStateAll,IMStateNone,IMKeyActionPress, EntSelect,data);
	IMRegisterForwardFilter('-','-'+1,IMStateAll,IMStateNone,IMKeyActionPress, ChnPage,data);
	IMRegisterForwardFilter('=','='+1,IMStateAll,IMStateNone,IMKeyActionPress, ChnPage,data);
	IMReadConfigure("IconPath",IMConfString,&buf,NULL,data);
	if(buf) IMSetTrayIcon(buf,data);
	return 0;
}

MetaDataBegin(English,IC,1)
	IMSetMetaData(NCandidates,10);
	IMRegisterEventHandler(OnInitialize,Init);
	IMRegisterEventHandler(OnEnabled, Enabled);
	IMRegisterEventHandler(OnPreeditTextChanged,InputChanged);
MetaDataEnd
